package grabana

import (
	
	
	
	
	
	
	

	
	
)

// ErrDashboardNotFound is returned when the given dashboard can not be found.
var ErrDashboardNotFound = errors.New("dashboard not found")

// Dashboard represents a Grafana dashboard.
type Dashboard struct {
	ID          int      `json:"id"`
	UID         string   `json:"uid"`
	Title       string   `json:"title"`
	URL         string   `json:"url"`
	Tags        []string `json:"tags"`
	IsStarred   bool     `json:"isStarred"`
	FolderID    int      `json:"folderId"`
	FolderUID   string   `json:"folderUid"`
	FolderTitle string   `json:"folderTitle"`
	FolderURL   string   `json:"folderUrl"`
}

// GetDashboardByTitle finds a dashboard, given its title.
func ( *Client) ( context.Context,  string) (*Dashboard, error) {
	,  := .get(, "/api/search?type=dash-db&query="+url.QueryEscape())
	if  != nil {
		return nil, 
	}

	defer func() { _ = .Body.Close() }()

	if .StatusCode != http.StatusOK {
		return nil, .httpError()
	}

	var  []Dashboard
	if  := decodeJSON(.Body, &);  != nil {
		return nil, 
	}

	if len() == 0 {
		return nil, ErrDashboardNotFound
	}

	for  := range  {
		if strings.EqualFold([].Title, ) {
			return &[], nil
		}
	}

	return nil, ErrDashboardNotFound
}

// rawDashboardByUID finds a dashboard, given its UID.
func ( *Client) ( context.Context,  string) (*sdk.Board, error) {
	,  := .get(, "/api/dashboards/uid/"+url.PathEscape())
	if  != nil {
		return nil, 
	}

	defer func() { _ = .Body.Close() }()

	if .StatusCode == http.StatusNotFound {
		return nil, ErrDashboardNotFound
	}
	if .StatusCode != http.StatusOK {
		return nil, .httpError()
	}

	 := struct {
		 sdk.Board `json:"dashboard"`
	}{}

	if  := decodeJSON(.Body, &);  != nil {
		return nil, 
	}

	return &., nil
}

// UpsertDashboard creates or replaces a dashboard, in the given folder.
func ( *Client) ( context.Context,  *Folder,  dashboard.Builder) (*Dashboard, error) {
	// first pass: save the new dashboard
	,  := .persistDashboard(, , )
	if  != nil {
		return nil, 
	}

	,  := .rawDashboardByUID(, .UID)
	if  != nil {
		return nil, 
	}

	// second pass: delete existing alerts associated to that dashboard
	,  := .listAlertsForDashboard(, .UID)
	if  != nil {
		return nil, fmt.Errorf("could not prepare deletion of previous alerts for dashboard: %w", )
	}
	for ,  := range  {
		if  := .DeleteAlertGroup(, .Namespace, .RuleGroup);  != nil {
			return nil, fmt.Errorf("could not delete previous alerts for dashboard: %w", )
		}
	}

	// third pass: create new alerts
	 := .Alerts()

	// If there are no alerts to create, we can return early
	if len() == 0 {
		return , nil
	}

	,  := .datasourcesUIDMap()
	if  != nil {
		return nil, 
	}

	for  := range  {
		 := *[]

		.HookDashboardUID(.UID)
		.HookPanelID(panelIDByTitle(, .Builder.Name))

		if  := .AddAlert(, .Title, , );  != nil {
			return nil, fmt.Errorf("could not add new alerts for dashboard: %w", )
		}
	}

	return , nil
}

func ( *Client) ( context.Context,  *Folder,  dashboard.Builder) (*Dashboard, error) {
	,  := json.Marshal(struct {
		 *sdk.Board `json:"dashboard"`
		  uint       `json:"folderId"`
		 bool       `json:"overwrite"`
	}{
		: .Internal(),
		:  .ID,
		: true,
	})
	if  != nil {
		return nil, 
	}

	,  := .sendJSON(, http.MethodPost, "/api/dashboards/db", )
	if  != nil {
		return nil, 
	}

	defer func() { _ = .Body.Close() }()

	if .StatusCode != http.StatusOK {
		return nil, .httpError()
	}

	var  Dashboard
	if  := decodeJSON(.Body, &);  != nil {
		return nil, 
	}

	return &, nil
}

// DeleteDashboard deletes a dashboard given its UID.
func ( *Client) ( context.Context,  string) error {
	// first: delete existing alerts associated to that dashboard
	,  := .listAlertsForDashboard(, )
	if  != nil {
		return fmt.Errorf("could not prepare deletion of alerts for dashboard: %w", )
	}
	for ,  := range  {
		if  := .DeleteAlertGroup(, .Namespace, .RuleGroup);  != nil {
			return fmt.Errorf("could not delete alerts for dashboard: %w", )
		}
	}

	// then: delete the dashboard itself
	,  := .delete(, "/api/dashboards/uid/"+)
	if  != nil {
		return 
	}

	defer func() { _ = .Body.Close() }()

	if .StatusCode == http.StatusNotFound {
		return ErrDashboardNotFound
	}
	if .StatusCode != http.StatusOK {
		return .httpError()
	}

	return nil
}

func panelIDByTitle( *sdk.Board,  string) string {
	for ,  := range .Rows {
		for ,  := range .Panels {
			if .Title ==  {
				return fmt.Sprintf("%d", .ID)
			}
		}
	}

	for ,  := range .Panels {
		if .Title ==  {
			return fmt.Sprintf("%d", .ID)
		}
	}

	return ""
}